package gsw.tool.eventbus.core;

import android.content.Context;
import android.support.annotation.NonNull;

import com.google.gson.Gson;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import gsw.tool.eventbus.annotion.ClassId;
import gsw.tool.eventbus.eventbus.EventBus;
import gsw.tool.eventbus.request.Request;
import gsw.tool.eventbus.request.RequestBean;
import gsw.tool.eventbus.request.RequestParameter;
import gsw.tool.eventbus.response.Response;
import gsw.tool.eventbus.service.HermesService;
import gsw.tool.eventbus.util.TypeUtils;

/**
 * 跨进程通信框架
 */
public class Hermes {
    //得到对象
    public static final int TYPE_NEW = 0;
    //得到单例
    public static final int TYPE_GET = 1;

    private static final Hermes ourInstance = new Hermes();
    private Context sContext;
    //服务端注册的类
    private TypeCenter typeCenter;
    private ServiceConnectionManager serviceConnectionManager;
    Gson GSON = new Gson();

    public static synchronized Hermes getDefault() {
        return ourInstance;
    }

    private Hermes() {
        serviceConnectionManager = ServiceConnectionManager.getInstance();
        typeCenter = TypeCenter.getInstance();
    }

    /**
     * 初始化
     *
     * @param context 上下文
     */
    public void init(Context context) {
        sContext = context.getApplicationContext();
    }

    /**
     * 初始化EventBus
     *
     * @param context 上下文
     */
    public void initEventBus(Context context) {
        sContext = context.getApplicationContext();
        register(EventBus.class);
    }

    /**
     * 在服务端注册类，供客户端访问
     *
     * @param clazz 注册的类
     */
    public void register(Class<?> clazz) {
        typeCenter.register(clazz);
    }

    /**
     * 在客户端连接服务HermesService
     *
     * @param context 上下文
     * @param service Hermes的远程服务
     */
    public void connect(Context context, Class<? extends HermesService> service) {
        connectApp(context, null, service);
    }

    /**
     * 在客户端连接服务HermesService
     *
     * @param context     上下文
     * @param packageName 服务端的包名
     * @param service     Hermes的远程服务
     */
    public void connectApp(Context context, String packageName, Class<? extends HermesService> service) {
        //初始化客户端
        init(context);
        //连接远程服务
        serviceConnectionManager.bind(context.getApplicationContext(), packageName, service);
    }

    /**
     * 获取服务端的代理对象：单例
     *
     * @param clazz      对象的类型
     * @param parameters 参数
     */
    public <T> T getInstance(Class<T> clazz, Object... parameters) {
        sendRequest(HermesService.class, clazz, null, parameters);
        return getProxy(HermesService.class, clazz);
    }

    /**
     * 获取服务端的代理对象，通过代理对象实现跨进程通信
     *
     * @param service HermesService
     * @param clazz   对象的类型
     */
    private <T> T getProxy(Class<? extends HermesService> service, Class clazz) {
        ClassLoader classLoader = service.getClassLoader();
        T proxy = (T) Proxy.newProxyInstance(classLoader, new Class<?>[]{clazz}, new HermesInvocationHandler(service, clazz));
        return proxy;
    }

    /**
     * 向服务端发送请求：生成单例对象
     *
     * @param hermesServiceClass HermesService
     * @param clazz              请求的类
     * @param method             请求的方法
     * @param parameters         请求的方法的参数
     */
    private <T> Response sendRequest(Class<HermesService> hermesServiceClass, Class<T> clazz, Method method, Object[] parameters) {
        RequestBean requestBean = getRequestBean(clazz, method, parameters);
        //生成JSON格式的请求：单例类型
        Request request = new Request(GSON.toJson(requestBean), TYPE_GET);
        return serviceConnectionManager.request(hermesServiceClass, request);
    }

    /**
     * 向服务端发送请求：普通请求
     *
     * @param hermesServiceClass HermesService
     * @param clazz              请求的类
     * @param method             请求的方法
     * @param parameters         请求的方法的参数
     */
    public <T> Response sendObjectRequest(Class<HermesService> hermesServiceClass, Class<T> clazz, Method method, Object[] parameters) {
        RequestBean requestBean = getRequestBean(clazz, method, parameters);
        //生成JSON格式的请求：普通对象
        Request request = new Request(GSON.toJson(requestBean), TYPE_NEW);
        return serviceConnectionManager.request(hermesServiceClass, request);
    }

    /**
     * 生成请求数据
     *
     * @param clazz      请求的类
     * @param method     请求的方法
     * @param parameters 请求的方法的参数
     */
    @NonNull
    private <T> RequestBean getRequestBean(Class<T> clazz, Method method, Object[] parameters) {
        //请求数据
        RequestBean requestBean = new RequestBean();

        //如果clazz没有注解，则使用clazz的全类名
        if (clazz.getAnnotation(ClassId.class) == null) {
            //请求的类名
            requestBean.setClassName(clazz.getName());
            //请求结果的类名
            requestBean.setResultClassName(clazz.getName());
        } else {//如果clazz有注解，则使用注解的类名
            requestBean.setClassName(clazz.getAnnotation(ClassId.class).value());
            requestBean.setResultClassName(clazz.getAnnotation(ClassId.class).value());
        }
        //设置请求的方法名
        if (method != null) {
            requestBean.setMethodName(TypeUtils.getMethodId(method));
        }

        /**
         * 将请求的参数转为JSON格式的请求，这里使用GSON，因为使用FastJson会有问题。
         */
        RequestParameter[] requestParameters = null;
        if (parameters != null && parameters.length > 0) {
            requestParameters = new RequestParameter[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Object parameter = parameters[i];
                String parameterClassName = parameter.getClass().getName();
                String parameterValue = GSON.toJson(parameter);
                RequestParameter requestParameter = new RequestParameter(parameterClassName, parameterValue);
                requestParameters[i] = requestParameter;
            }
        }

        //设置请求方法的参数
        if (requestParameters != null) {
            requestBean.setRequestParameter(requestParameters);
        }
        return requestBean;
    }

    /**
     * 在服务端反注册类
     *
     * @param clazz 反注册的类
     */
    public void unRegister(Class<?> clazz) {
    }
}
